#include "Board.h"
#include "board_geom.h"


const float Board::K_BOARD_RENDER_SIZE(8.0);

Board::Board(std::string board) {
	std::vector<Cell> row;
	std::stringstream sstr;
	sstr << board;
	Cell cell;
	int temp;
	for (int y = 0; y < 8 ; y++) {
		for (int x = 0; x < 8 ; x++) {
			sstr >> temp;
			if(temp == 0)
				row.push_back(Cell());
			if(temp == 1)
				row.push_back(Cell(new Player_one_pawn(x,y)));
			if(temp == 2)
				row.push_back(Cell(new Player_two_pawn(x,y)));
		}

		this->m_board_cells.push_back(row);
		row.clear();
	}
	
	//get_board_cell(Vector2D(1,2)) = Cell(new Player_two_pawn(1,2)); // TO DELETE
	anim_xi=anim_xf=anim_yf=anim_yi=selec_x=selec_y=cursor_x=cursor_y=-1;
	m_one_graveyard=m_two_graveyard=0;
	m_board_next=m_board_cells;
	m_player_turn=1;
	save_board_state();
}

Board::Board() {
	std::vector<Cell> row;
	Cell cell;
	for (int y = 0; y < 8 ; y++) {
		for (int x = 0; x < 8 ; x++) {
			if (y < 2) {
				row.push_back(Cell(new Player_one_pawn(x,y)));
			} else if (y < 6) {
				row.push_back(Cell());
			} else {
				row.push_back(Cell(new Player_two_pawn(x,y)));
			}
		}

		this->m_board_cells.push_back(row);
		row.clear();
	}
	
	//get_board_cell(Vector2D(1,2)) = Cell(new Player_two_pawn(1,2)); // TO DELETE
	anim_xi=anim_xf=anim_yf=anim_yi=selec_x=selec_y=cursor_x=cursor_y=-1;
	m_one_graveyard=m_two_graveyard=0;
	m_board_next=m_board_cells;
	m_player_turn=1;
	save_board_state();
}

Board::~Board() {
}

void Board::draw_s_content(float separation_width, Vector2D v){
	if(get_board_cell(Vector2D(v.x, v.y)).has_pawn()){
		glPushMatrix();

		glTranslatef(separation_width*(v.x + 1  +get_board_cell(v).get_game_object()->get_inc_x()),
					get_board_cell(v).get_game_object()->get_inc_z(),
					separation_width*(v.y + 1  +get_board_cell(v).get_game_object()->get_inc_y()));	

		glTranslatef(0,get_board_cell(v).get_game_object()->get_rot_axis(),0);
		glRotatef(get_board_cell(v).get_game_object()->get_rot_ang(),
			get_board_cell(v).get_game_object()->get_rot_x(),
			get_board_cell(v).get_game_object()->get_rot_y(),
			get_board_cell(v).get_game_object()->get_rot_z());
		glTranslatef(0,-get_board_cell(v).get_game_object()->get_rot_axis(),0);

		get_board_cell(v).render_contents(GL_RENDER);
		glPopMatrix();
	}
}

void draw_square(int x, int y, float s){
	glPushMatrix();
	glTranslatef(s*(x+1),0,s*(y+1));
	s/=2.0;
	/*
	glEnable(GL_COLOR_MATERIAL);
	if(x%2==0 || y%2==0)
		glColor3d(1,0,0);
	else
		glColor3d(0,1,0);
		*/
	float l=0.0;	
	glBegin(GL_POLYGON);
	glNormal3f(0,1,0);
	glVertex3f(-s,l,-s);
	glVertex3f(-s,l,s);
	glVertex3f(s,l,s);
	glVertex3f(s,l,-s);
	glEnd();
	glPopMatrix();
	//glDisable(GL_COLOR_MATERIAL);
}

void Board::draw_option_square(int x, int y, float s, int own){
	glPushMatrix();
	glTranslatef(s*(x+1),0,s*(y+1));
	s/=2.0;

	float opa= 0.5;
	if(own==1)
		Material_components (0.0,0.0,0.0,opa,  0.0,0.0,0.0,opa,  0.05,0.73,0.95,opa,  32).act();
	else if(own==0)
		if(get_board_cell(Vector2D(x,y)).has_pawn())
			Material_components (0.0,0.0,0.0,opa,  0.0,0.0,0.0,opa,  0.85,0.08,0.08,opa,  32).act();
		else
			Material_components (0.0,0.0,0.0,opa,  0.0,0.0,0.0,opa,  0.95,0.83,0.08,opa,  32).act();
	else
		Material_components (0.0,0.0,0.0,opa,  0.0,0.0,0.0,opa,  0.15,0.83,0.08,opa,  32).act();

	glPolygonMode(GL_FRONT, GL_FILL);
	float h=0.07;
	glBegin(GL_POLYGON);
	glNormal3f(0,1,0);
	glVertex3f(-s,h,-s);
	glVertex3f(-s,h,s);
	glVertex3f(s,h,s);
	glVertex3f(s,h,-s);
	glEnd();
	glPopMatrix();

	glPushMatrix();
	glTranslatef(s*2*(x+1),0,s*2*(y+1));

	opa= 1;
	if(own==1)
		Material_components (0.0,0.0,0.0,opa,  0.0,0.0,0.0,opa,  0.05,0.73,0.95,opa,  32).act();
	else if(own==0)
		if(get_board_cell(Vector2D(x,y)).has_pawn())
			Material_components (0.0,0.0,0.0,opa,  0.0,0.0,0.0,opa,  0.85,0.08,0.08,opa,  32).act();
		else
			Material_components (0.0,0.0,0.0,opa,  0.0,0.0,0.0,opa,  0.95,0.83,0.08,opa,  32).act();
	else
		Material_components (0.0,0.0,0.0,opa,  0.0,0.0,0.0,opa,  0.15,0.83,0.08,opa,  32).act();

	glPolygonMode(GL_FRONT, GL_LINE);
	glBegin(GL_POLYGON);
	glNormal3f(0,1,0);
	glVertex3f(-s,h,-s);
	glVertex3f(-s,h,s);
	glVertex3f(s,h,s);
	glVertex3f(s,h,-s);
	glEnd();
	glPopMatrix();

	glPolygonMode(GL_FRONT, GL_FILL);
	
}

void Board::draw_select_content(float separation_width, Vector2D v){
	if(get_board_cell(Vector2D(v.x, v.y)).has_pawn()){

		//glBlendFunc(GL_ONE, GL_ONE);
		
		glPushMatrix();
			glTranslatef(separation_width*(v.x + 1), 0,	separation_width*(v.y + 1));	
			get_board_cell(v).render_contents(GL_RENDER);
		glPopMatrix();
		
		draw_option_square(v.x,v.y,separation_width,1);

		get_board_cell(v).get_game_object().get()->get_material().reset();
		get_board_cell(v).get_game_object().get()->get_material().set_opac(1);
	}
}

void Board::draw_graveyard(int side){
	float amount,pos,vpos;
	static const float separation_width = 0.58;
	Pawn *p;
	
	vpos=4.3;
	if(side==1){
		
		amount=m_one_graveyard;
		pos=4.6;
		p=new Player_one_pawn(0,0);
	}
	else{
		amount=m_two_graveyard;
		pos=-4.6;
		p=new Player_two_pawn(0,0);
	}

	
	glPushMatrix();
	if(side==2)
		glScalef(1,1,-1);
	

	for(int i=0; i< amount; ++i){

		float shift=0;
		if(i%2==0)
			shift=0.5;///0.3;
		if(side==1)
			shift*=-1;

		glPushMatrix();
			
			glTranslatef(pos+shift,0,i*separation_width-vpos);
			
			if(side==2)
				glScalef(1,1,-1);
			
			p->render(GL_RENDER);
		glPopMatrix();
	}

	
	glPopMatrix();
	
	delete p;
}

void Board::render( GLenum mode ) {
	static const float separation_width = K_BOARD_RENDER_SIZE/(K_BOARD_RENDER_SIZE+1);
	
	if(mode == GL_RENDER){
		draw_graveyard(1);
		draw_graveyard(2);
	}

	glPushMatrix();

	glTranslatef(0.4,0.7,0.4);
		
	if(mode == GL_RENDER)
		render_board();
	
	glTranslatef(-5*separation_width,-1*separation_width,-5*separation_width);

	if (mode == GL_SELECT)
		glPushName(0);

	for (int x = 0; x < get_board().size(); ++x) {
		for (int y = 0; y < get_board_row(0).size(); ++y) {

			if(mode == GL_SELECT){
				
				glLoadName(x);
				glPushName(y);
				draw_square(x,y,separation_width);
			}
				
			if(get_board_cell(Vector2D(x, y)).has_pawn() && !((x == anim_xi && y==anim_yi) || (x == anim_xf && y==anim_yf) || (x == selec_x && y==selec_y))){
				glPushMatrix();

					if(x==1 && y==1)
						;//printf("A:%f\n",);
					glTranslatef(separation_width*(x + 1  +get_board_cell(Vector2D(x, y)).get_game_object()->get_inc_x()),
														   get_board_cell(Vector2D(x, y)).get_game_object()->get_inc_z(),
								 separation_width*(y + 1  +get_board_cell(Vector2D(x, y)).get_game_object()->get_inc_y()));	

					get_board_cell(Vector2D(x, y)).render_contents(mode);
				glPopMatrix();
			}	

			if(mode == GL_SELECT)
				glPopName();	

		}
	}

	if(mode == GL_SELECT)
		glPopName();	

	if(mode == GL_RENDER){
		if(anim_xi != -1) 
			draw_s_content(separation_width,Vector2D(anim_xi,anim_yi));
			
		if(anim_xf != -1) 
			draw_s_content(separation_width,Vector2D(anim_xf,anim_yf));
			
		if(selec_x != -1)
			draw_select_content(separation_width,Vector2D(selec_x,selec_y));

		draw_valid_squares(separation_width);

		if(cursor_x != -1)
			draw_option_square(cursor_x,cursor_y,separation_width,2);
	}

	glPopMatrix();
}

void Board::render_only_board( )
{
	static const float separation_width = K_BOARD_RENDER_SIZE/(K_BOARD_RENDER_SIZE+1);

	glPushMatrix();
	glTranslatef(0.4,0.7,0.4);
	glTranslatef(-5*separation_width,-1*separation_width,-5*separation_width);

	glPushName(0);

	for (int x = 0; x < get_board().size(); ++x) {
		for (int y = 0; y < get_board_row(0).size(); ++y) {

			glLoadName(x);
			glPushName(y);	
			draw_square(x,y,separation_width);


			if(get_board_cell(Vector2D(x, y)).has_pawn() && !((x == anim_xi && y==anim_yi) || (x == anim_xf && y==anim_yf) || (x == selec_x && y==selec_y))){
				glPushMatrix();
				glTranslatef(separation_width*(x + 1  +get_board_cell(Vector2D(x, y)).get_game_object()->get_inc_x()),
					get_board_cell(Vector2D(x, y)).get_game_object()->get_inc_z(),
					separation_width*(y + 1  +get_board_cell(Vector2D(x, y)).get_game_object()->get_inc_y()));	

				get_board_cell(Vector2D(x, y)).render_contents(GL_SELECT);
				glPopMatrix();
			}	

			glPopName();
		}
	}
	glPopName();
	glPopMatrix();
}

void Board::draw_valid_squares(float s)
{
	if(valid_squares.size() == 0)
		return;

	for(int i=0; i<valid_squares.size(); ++i){
		draw_option_square(valid_squares[i].x,valid_squares[i].y,s);
	}
}


void Board::render_board() {
	Material_components (0.4,0.4,0.4,1.0,  0.6,0.6,0.6,1.0,  0.4,0.4,0.4,1.0,  32).act();

	glEnable(GL_TEXTURE_2D);
	glBindTexture(GL_TEXTURE_2D,2);
	glCallList(3);
	glDisable(GL_TEXTURE_2D);

}

std::vector<std::vector<Cell> > Board::get_board() const {
	return m_board_cells;
}

void Board::set_board( std::vector<std::vector<Cell> > board ) {
	m_board_cells = board;
}

void Board::clear_board() {
	m_board_cells.clear();
}

std::vector<Cell> Board::get_board_row(unsigned int row_index) const {
	return get_board()[row_index];
}

void Board::save_board_state()
{
	m_board_states.push_back(m_board_next);
}

bool Board::load_previous_state()
{
	if(m_board_states.size()<2)
		return false;

	if(m_board_states.size()>1)
		m_board_states.pop_back();
	m_board_cells= (*m_board_states.rbegin());
	m_board_next=m_board_cells;

	m_move_history.pop_back();

	if(m_graveyard_history.size()>0)
		m_graveyard_history.pop_back();

	if(m_graveyard_history.size()> 0){
		m_one_graveyard=(*m_graveyard_history.rbegin()).first;
		m_two_graveyard=(*m_graveyard_history.rbegin()).second;
	}
	else
	{
		m_one_graveyard=0;
		m_two_graveyard=0;
	}
	anim_xi=anim_xf=anim_yf=anim_yi=-1;

	change_turn();

	return true;
}

bool Board::animate_obj_at(int xi, int yi, int xf, int yf, float t )
{
	anim_xi=xi;
	anim_xf=xf;
	anim_yf=yf;
	anim_yi=yi;
	if(get_board_cell(Vector2D(xi,yi)).has_pawn()){ /////CAN BE REMOVED
		if(get_board_cell(Vector2D(xf,yf)).has_pawn())
		{
			bool v1 = get_board_cell(Vector2D(xi,yi)).get_game_object()->animate_capture(xi,yi,xf,yf,t);
			bool v2 = get_board_cell(Vector2D(xf,yf)).get_game_object()->animate_cought(xi,yi,xf,yf,t,m_one_graveyard,m_two_graveyard);
			
			if (v1 || v2)
				return true;
			else
			{
				anim_xi=anim_xf=anim_yf=anim_yi=-1;
				return false;
			}

		}
		else
		{
			return get_board_cell(Vector2D(xi,yi)).get_game_object()->animate(xi,yi,xf,yf,t);
		}
	}										 /////CAN BE REMOVED
	else									 /////CAN BE REMOVED
		return false;						 /////CAN BE REMOVED
}


Cell& Board::get_board_cell( Vector2D pos ) {
		return m_board_cells[pos.y][pos.x];
}

Cell& Board::get_next_board_cell(Vector2D pos)
{
	return m_board_next[pos.y][pos.x];
}

void Board::set_board_cell(Vector2D pos, Cell c)
{
	m_board_cells[pos.y][pos.x] = c;
}

void Board::move_piece( int xi, int yi, int xf, int yf )
{
	m_board_next=m_board_cells;
	if(get_next_board_cell(Vector2D(xi,yi)).has_pawn()){

		get_next_board_cell(Vector2D(xf,yf)).set_game_object( get_next_board_cell(Vector2D(xi,yi)).get_game_object() );
		get_next_board_cell(Vector2D(xi,yi)).set_game_object(nullptr);

	}
}

void Board::update_board(int xi, int yi, int xf, int yf)
{
	if(get_board_cell(Vector2D(xi,yi)).has_pawn()){
		if(get_board_cell(Vector2D(xf,yf)).has_pawn()){
			if(get_board_cell(Vector2D(xf,yf)).get_game_object().get()->get_ownership()==1)
				m_one_graveyard++;
			else
				m_two_graveyard++;
		}
	}

	m_board_cells=m_board_next;
	change_turn();
	save_move(xi,yi,xf,yf,m_one_graveyard,m_two_graveyard);
	save_board_state();
}

std::pair<Vector2D,Vector2D> Board::load_previous_move()
{
	if(m_move_history.size()>0)
		return m_move_history[m_move_history.size()-1];
	else
		return std::pair<Vector2D,Vector2D>(Vector2D(),Vector2D());
}

void Board::save_move(int xi, int yi, int xf, int yf, int one_g, int two_g)
{
	m_move_history.push_back(std::pair<Vector2D,Vector2D>(Vector2D(xi,yi),Vector2D(xf,yf)));
	m_graveyard_history.push_back(std::pair<int,int>(one_g,two_g));
}

void Board::create_board_geom( float scale )
{
	glNewList(3,GL_COMPILE);
	glEnable(GL_NORMALIZE);
	glPushMatrix();

	glTranslatef(-0.89*scale,-4.12*scale,-0.89*scale);
	glRotated(-45,0,1,0);
	glScaled(2.534*scale,2.534*scale,2.534*scale);

	for(int i=0; i< B_FACES_COUNT*3; i+=3){
		glBegin(GL_TRIANGLES);
		glNormal3f(bvertexs[bindexes[i]].nx,bvertexs[bindexes[i]].ny,bvertexs[bindexes[i]].nz);
		glTexCoord2f(bvertexs[bindexes[i]].u,bvertexs[bindexes[i]].v);
		glVertex3f(bvertexs[bindexes[i]].x,bvertexs[bindexes[i]].y,bvertexs[bindexes[i]].z);

		glNormal3f(bvertexs[bindexes[i+1]].nx,bvertexs[bindexes[i+1]].ny,bvertexs[bindexes[i+1]].nz);
		glTexCoord2f(bvertexs[bindexes[i+1]].u,bvertexs[bindexes[i+1]].v);
		glVertex3f(bvertexs[bindexes[i+1]].x,bvertexs[bindexes[i+1]].y,bvertexs[bindexes[i+1]].z);

		glNormal3f(bvertexs[bindexes[i+2]].nx,bvertexs[bindexes[i+2]].ny,bvertexs[bindexes[i+2]].nz);
		glTexCoord2f(bvertexs[bindexes[i+2]].u,bvertexs[bindexes[i+2]].v);
		glVertex3f(bvertexs[bindexes[i+2]].x,bvertexs[bindexes[i+2]].y,bvertexs[bindexes[i+2]].z);
		glEnd();
	}
	glPopMatrix();
	glDisable(GL_NORMALIZE);
	glEndList();
}

void Board::init_geoms( float scale )
{
	Board::create_board_geom(scale);
	Pawn::create_pawn_low_geometry(scale);
	Pawn::create_pawn_high_geometry(scale);
	
}

void Board::set_selected( int x, int y )
{
	////TEMP
	//TODO REMOVE
	/*
	valid_squares.push_back(Vector2D(x,y+1));
	if(x<7)
	valid_squares.push_back(Vector2D(x+1,y+1));
	if(x>0)
	valid_squares.push_back(Vector2D(x-1,y+1));
	////
	*/
	selec_x=x;
	selec_y=y;
}

void Board::deselect()
{
	clear_valid_squares();
	selec_x=selec_y=-1;
}

void Board::set_valid_squares( std::vector<Vector2D> vec )
{
	valid_squares = vec;
}

void Board::clear_valid_squares()
{
	valid_squares.clear();
}

void Board::set_cursor( int x, int y )
{
	cursor_x=x;
	cursor_y=y;
}

bool Board::is_valid_cell( int x, int y )
{
	for(int i=0; i<valid_squares.size(); ++i){
		printf("%d %d\n",valid_squares[i].x,valid_squares[i].y);
		if(valid_squares[i].x==x && valid_squares[i].y==y)
			return true;}
	return false;
}

int Board::change_turn()
{
	if(m_player_turn==1)
		m_player_turn=2;
	else
		m_player_turn=1;
	return m_player_turn;
}

int Board::get_turn()
{
	return m_player_turn;
}

std::string Board::to_string() {
	std::stringstream sstr;

	sstr << '[';
	for(int y = 0; y < m_board_next.size(); ++y) {
		sstr << '[';
		for(int x = 0; x < m_board_next[0].size(); ++x) {
			if(m_board_next[y][x].get_game_object() != nullptr) {
				sstr << m_board_next[y][x].get_game_object()->get_ownership();
			} else {
				sstr << 0;
			}
			
			if(x != m_board_next[0].size() - 1) {
				sstr << ',';
			}
		}
		sstr << ']';

		if(y != m_board_next.size() - 1) {
			sstr << ',';
		}
	}

	sstr << ']';

	return sstr.str();
}

void Board::set_player_type( int p, int t )
{
	if(p==1)
		m_one_type=t;
	else
		m_two_type=t;
}

bool Board::is_human()
{
	if(m_player_turn==1)
		return (m_one_type==0);
	else
		return (m_two_type==0);
}

bool Board::is_next_human()
{
	if(m_player_turn==2)
		return (m_one_type==0);
	else
		return (m_two_type==0);
}

int Board::get_player_type( int p )
{
	if(p==1)
		return m_one_type;
	else
		return m_two_type;
}

int Board::get_next_turn()
{
	if(m_player_turn==1)
		return 2;
	else
		return 1;
}

int Board::get_move_history_count()
{
	return m_move_history.size();
}

std::pair<Vector2D,Vector2D> Board::get_move_history_at( int d )
{
	if(d >= m_move_history.size())
		return std::pair<Vector2D,Vector2D>(Vector2D(),Vector2D());
	return m_move_history[d];
}

void Board::reset()
{
	m_board_cells=m_board_next=m_board_states[0];
	m_one_graveyard=m_two_graveyard=0;
}


